package com.springboot.mongo.config;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.springboot.common.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoTypeMapper;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

/**
 * mongo配置
 *
 * @author supu
 * @date 2019-08-26 11:21
 **/
public class MongoConfiguration {

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public MongoTemplate mongoTemplate(MongoConfigurer mongoConfigurer) {
        if (mongoConfigurer == null || !mongoConfigurer.validate()) {
            System.out.println("mongoConfigurer must be not null or mongoConfigurer property must be not null");
            return null;
        }
        MongoDbFactory mongoDbFactory = buildMongoDbFactory(mongoConfigurer);
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory,
                mongoConfigurer.isConvertWithClassInfo() ? null : withoutClassInfoConverter(mongoDbFactory));
        if (mongoConfigurer.getWriteConcernResolver() != null) {
            mongoTemplate.setWriteConcernResolver(mongoConfigurer.getWriteConcernResolver());
        }
        return mongoTemplate;
    }

    private MongoDbFactory buildMongoDbFactory(MongoConfigurer mongoConfigurer) {
        List<ServerAddress> list = new ArrayList<>();

        if (StringUtil.isNotBlank(mongoConfigurer.getHost())) {
            JSONArray array = JSONArray.parseArray(mongoConfigurer.getHost());
            for (int i = 0; i < array.size(); i++) {
                JSONObject obj = array.getJSONObject(i);
                ServerAddress sap = new ServerAddress(obj.getString("ip"), obj.getIntValue("port"));
                list.add(sap);
            }
        }
        if (list.size() < 1) {
            // 默认数据库地址
            ServerAddress sap0 = new ServerAddress("192.168.1.235", 27017);
            list.add(sap0);
        }

        ReadPreference readPreference = buildReadPreference(mongoConfigurer.getReadPreference());

        MongoClientOptions mongoClientOptions = MongoClientOptions.builder()
                .connectTimeout(mongoConfigurer.getConnectTimeout())
                .socketTimeout(mongoConfigurer.getSocketTimeout())
                .readPreference(readPreference)
                .connectionsPerHost(mongoConfigurer.getConnectionsPerHost())
                .maxWaitTime(mongoConfigurer.getMaxWaitTime())
                .threadsAllowedToBlockForConnectionMultiplier(mongoConfigurer.getThreadsAllowedToBlockForConnectionMultiplier())
                .writeConcern(mongoConfigurer.getWriteConcern())
                .requiredReplicaSetName(mongoConfigurer.getReplicaSetName())
                .build();

        MongoClient mongoClient;
        if (StringUtil.isNoneBlank(mongoConfigurer.getUserName(), mongoConfigurer.getPassword())) {
            MongoCredential credential = MongoCredential.createCredential(mongoConfigurer.getUserName(), mongoConfigurer.getDataBaseName(),
                    mongoConfigurer.getPassword().toCharArray());
            mongoClient = new MongoClient(list, credential, mongoClientOptions);
        } else {
            mongoClient = new MongoClient(list, mongoClientOptions);
        }
        return new SimpleMongoDbFactory(mongoClient, mongoConfigurer.getDataBaseName());
    }


    private ReadPreference buildReadPreference(String readPreferenceStr) {
        ReadPreference readPreference;

        switch (readPreferenceStr) {
            case "primary":
                readPreference = ReadPreference.primary();
                break;
            case "primaryPreferred":
                readPreference = ReadPreference.primaryPreferred();
                break;
            case "secondary":
                readPreference = ReadPreference.secondary();
                break;
            case "secondaryPreferred":
                readPreference = ReadPreference.secondaryPreferred();
                break;
            case "nearest":
                readPreference = ReadPreference.nearest();
                break;
            default:
                readPreference = ReadPreference.primaryPreferred();
                break;
        }
        return readPreference;
    }

    private MongoConverter withoutClassInfoConverter(MongoDbFactory mongoDbFactory) {
        // set type key null, 这样在 mongoDB 存储中将不带有 _class 字段
        MongoTypeMapper typeMapper = new DefaultMongoTypeMapper(null);
        MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), new MongoMappingContext());
        converter.setTypeMapper(typeMapper);
        return converter;
    }
}
